package com.codegeek.framework;

import com.alibaba.fastjson.JSON;
import com.codegeek.common.utils.DateUtils;
import com.codegeek.framework.ems.EmsServerUtil;
import com.codegeek.framework.mail.MailService;
import com.codegeek.framework.redis.RedisCache;
import com.codegeek.project.communicate.RedisDBService;
import com.codegeek.project.monitor.domain.SysOperLog;
import com.codegeek.project.monitor.service.ISysOperLogService;
import com.codegeek.project.slope.domain.*;
import com.codegeek.project.slope.service.*;
import org.apache.logging.log4j.LogManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Configuration
@EnableScheduling
@RestController
public class TaskTimer {

    public static final org.apache.logging.log4j.Logger logger = LogManager.getLogger("FileUtils");

    private static final Map<String, Integer> alarmLevelMap = new HashMap<>();

    @Autowired
    private ISlopeSensorService slopeSensorService;

    @Autowired
    private ISlopeAlarmService slopeAlarmService;

    @Autowired
    private ISlopeMonitorItemService slopeMonitorItemService;

    @Autowired
    private ISlopeItemHistoryService slopeItemHistoryService;

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Autowired
    RedisCache redisCache;

    @Autowired
    RedisDBService redisDBService;

    @Autowired
    ISlopeDeviceService slopeDeviceService;

    @Autowired
    private ISlopeSubscriberService slopeSubscriberService;

    @Autowired
    private ISlopeSubscribeDetailService slopeSubscribeDetailService;

    @Autowired
    private ISysOperLogService operLogService;

    @Autowired
    private EmsServerUtil emsServerUtil;

    @Autowired
    private ISlopeGlareService slopeGlareService;

    @Autowired
    private MailService mailService;
    private static String weatherUrl = "https://tianqiapi.com/api?version=v6&appid=12365866&appsecret=3osBAiLP&vue=1";


    //    @Scheduled(cron = "0 0/1 * * * ?")//每分钟执行一次
//    public void createData() throws Exception {
//        SlopeSensor sensor = new SlopeSensor();
//        List<SlopeSensor> list =  slopeSensorService.selectSlopeSensorList(sensor);
//        Integer gnssType=redisDBService.getGNSSTypeId();
//        for(SlopeSensor slopeSensor : list) {
//            List<SlopeMonitorItem> items = slopeSensor.getMonitorItems();
//            boolean bGNSS=false;
//            if(slopeSensor.getSensorTypeId().equals(gnssType)){
//                bGNSS=true;
//            }
//            for(SlopeMonitorItem item : items) {
//                if(bGNSS){
//                    handleGNSSItemValue(item,slopeSensor.getSensorId(),slopeSensor.getDevice().getImei());
//                }
//            }
//        }
//    }

    private static Map map = new HashMap();

    @Scheduled(cron = "0 0 0/3 * * ?")
    public void getWeather() {
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> res = restTemplate.getForEntity(weatherUrl, String.class);
        String body = res.getBody();
        Map mapTypes = JSON.parseObject(body);
        for (Object obj : mapTypes.keySet()) {
            if (obj.equals("wea_img")) {
                map.put("wea_img", mapTypes.get(obj));
            }
            if (obj.equals("wea")) {
                map.put("wea", mapTypes.get(obj));
            }
            if (obj.equals("tem1")) {
                map.put("tem1", mapTypes.get(obj));
            }
            if (obj.equals("tem2")) {
                map.put("tem2", mapTypes.get(obj));
            }
        }
    }

    public Map<String, Object> getWeatherMap() {
        if (map == null || map.isEmpty()) {
            getWeather();
        }
        return map;
    }

//    @Scheduled(cron = "0 0/1 * * * ?")//每分钟执行一次，传感器状态巡检,异步
    @Scheduled(cron = "0 0/1 * * * ?")//主服务器
//    @Scheduled(cron = "30 0/1 * * * ? ")//从服务器
    public void checkSensorData() throws Exception {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    List<SlopeSensor> sensorList = slopeSensorService.selectSlopeSensorList(new SlopeSensor());
                    for (SlopeSensor slopeSensor : sensorList) {
                        SlopeDevice slopeDevice = slopeSensor.getDevice();
                        int state = slopeDevice.getState();
                        if (state == 0 || state == 1) {
                            boolean isOnline = redisDBService.isSensorDeviceOnline(slopeSensor.getDevice().getImei());
                            int redisState = isOnline ? 1 : 0;
                            if (state != redisState) {
                                slopeDevice.setState(redisState);
                                slopeDevice.setOnlineTime(state == 1 ? null : new Date());
                                slopeDeviceService.updateSlopeDevice(slopeDevice);
                                //在线变为离线
                                if (state == 1) {
                                    System.out.println("设备离线推送");
                                    SlopeSubscriber subscriber = new SlopeSubscriber();
                                    subscriber.setIsValid("0");
                                    List<SlopeSubscriber> list = slopeSubscriberService.selectSlopeSubscriberList(subscriber);
                                    String content = getOutLineMessageInfo(DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss", new Date()), slopeDevice.getDeviceName(), slopeDevice.getNumber());
                                    for (SlopeSubscriber sub : list) {
                                        int type = sub.getSubscribeMethod();
                                        try {
                                            if (type == 1) {
                                                //手机
//                                                String code = emsServerUtil.sendMessage(sub.getSubscribePhone(), content);
//                                                if (!code.equals("200")) {
//                                                    throw new Exception();
//                                                }
                                            } else if (type == 2) {
                                                mailService.sendSimpleMail(sub.getSubscribeEmail(), slopeDevice.getDeviceName() + "设备离线", content);
                                                //邮箱
                                            } else if (type == 0) {
                                                //手机和邮箱
//                                                String code = emsServerUtil.sendMessage(sub.getSubscribePhone(), content);
                                                mailService.sendSimpleMail(sub.getSubscribeEmail(), slopeDevice.getDeviceName() + "设备离线", content);
//                                                if (!code.equals("200")) {
//                                                    throw new Exception();
//                                                }
                                            }
                                        } catch (Exception e) {
                                            SysOperLog log = new SysOperLog();
                                            log.setTitle("推送记录");
                                            log.setBusinessType(0);
                                            log.setMethod("com.coodgeek.project.communicate.RedisDBServuce");
                                            log.setOperatorType(1);
                                            log.setOperParam("{content:" + content + "}");
                                            log.setJsonResult("{msg:'告警发送给" + sub.getSubscribeNickName() + "失败'}");
                                            log.setOperTime(new Date());
                                            operLogService.insertOperlog(log);
                                        }
                                    }
                                }
                            }
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    logger.error(e);
                }
            }
        });

//        executorService.execute(new Runnable() {
//            @Override
//            public void run() {
//                try {
//                    List<SlopeGlare> slopeGlareList=slopeGlareService.selectOnlineSlopeGlareList(new SlopeGlare());
//                    for(SlopeGlare slopeGlare:slopeGlareList){
//                        String imei= slopeGlare.getDevice().getImei();
//                        int state=slopeGlare.getDevice().getState();
//                        if(state==0||state==1) {
//                            boolean isOnline = redisDBService.isDeviceOnline(imei);
//                            int newState = isOnline ? 1 : 0;
//                            if(state!=newState){
//                                SlopeDevice sd=slopeGlare.getDevice();
//                                sd.setState(newState);
//                                slopeDeviceService.updateSlopeDevice(sd);
//                            }
//                        }
//                    }
//                }catch (Exception e){
//                    e.printStackTrace();
//                }
//            }
//        });
    }

    @Scheduled(cron = "0 0 0/1 * * ?")//每小时执行一次，存储传感器历史数据 主服务器
//    @Scheduled(cron = "0 30 0/1 * * ?")//每小时执行一次，存储传感器历史数据 从服务器
    public void saveSensorHistory() {
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                SlopeSensor sensor = new SlopeSensor();
                List<SlopeSensor> list = slopeSensorService.selectSlopeSensorList(sensor);
                Integer gnssType = redisDBService.getGNSSTypeId();
                for (SlopeSensor slopeSensor : list) {
                    String imei = slopeSensor.getDevice().getImei();
                    List<SlopeMonitorItem> items = slopeSensor.getMonitorItems();
                    for (SlopeMonitorItem item : items) {
                        redisDBService.saveItemHistoryData(imei, item);
                    }
                    redisDBService.clearSensorHistory(imei);
                }
            }
        });
    }


    @Autowired
    private ISlopeCameraService slopeCameraService;

    @Scheduled(cron = "0 0/10 * * * ?")
//    @Scheduled(cron = "0 0/1 * * * ?") //1分钟
    public void getCameraStatus() {
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                List<SlopeCamera> list = slopeCameraService.selectSlopeCameraList(null);
                for (SlopeCamera slopeCamera : list) {
                    CameraParams cameraParams = new CameraParams();
                    cameraParams.setAccessToken(slopeCamera.getAccessToken());
                    cameraParams.setDeviceSerial(slopeCamera.getDevice().getNumber());
                    cameraParams.setChannelNo(1);
                    cameraParams.setDirection(-1);
                    cameraParams.setSpeed(1);

                    RestTemplate restTemplate = new RestTemplate();
                    HttpHeaders headers = new HttpHeaders();
                    headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
                    MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
                    map.add("accessToken", cameraParams.getAccessToken());
                    map.add("deviceSerial", cameraParams.getDeviceSerial());
                    map.add("channelNo", cameraParams.getChannelNo() + "");
                    String url = "https://open.ys7.com/api/lapp/device/ptz/stop";
                    HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
                    ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);
                    Map jsonMap = (Map) JSON.parse(response.getBody());
                    Integer code = Integer.parseInt(jsonMap.get("code").toString());
                    SlopeDevice slopeDevice = new SlopeDevice();
                    slopeDevice.setDeviceId(slopeCamera.getDeviceId());
                    if (code == 200 && slopeCamera.getDevice().getIsValid().equals("0")) {
                        // 在线
                        slopeDevice.setState(1);
                        slopeDeviceService.updateSlopeDevice(slopeDevice);
                    } else {
                        // 离线
                        slopeDevice.setState(0);
                        slopeDeviceService.updateSlopeDevice(slopeDevice);
                    }
                }
            }
        });
    }

    public void handleGNSSItemValue(SlopeMonitorItem item, Long sensorId, String imei) {
        Float itemValue = redisDBService.getMonitorItemData(imei, item.getItemName());
        if (itemValue != null) {
            Map<String, Object> map = redisCache.getCacheMap("sensorData_" + sensorId + ""); //读取当前以sensor的id为主键的值
            String arr;
            if (map.get(item.getItemName()) == null || map.get(item.getItemName()).equals("")) {
                arr = itemValue + "";
            } else {
                arr = map.get(item.getItemName()).toString();//获取以itemName为主键的数组值
                arr += "," + itemValue;
            }
            handleUpdateMoniterItem(item.getItemId(), itemValue);//todo 实际需要考虑是否需要此业务
            handleAlarm(item, itemValue, sensorId);//todo 判断当前设备值 是否含有报警业务，注意当前未写入推送业务
            map.put(item.getItemName(), arr);
            redisCache.setCacheMap("sensorData_" + sensorId + "", map);//再写入redis 刷新更新值

            //处理写入数据库业务
            String now = DateUtils.dateTimeNow(DateUtils.YYYY_MM_DD_HH_MM_SS);
            //1小时判断一次
            if (now.substring(14, 16).equals("00")) {
                insertHistory(item, sensorId);
            }
        }
    }

    /**
     * 传感器传输新的监测项的数据变化调用此方法
     *
     * @param item      监测项对象
     * @param itemValue 监测到的值
     * @param sensorId  传感器id
     * @throws Exception 此处为获取随机数异常，正常情况去掉
     */
    public void handleItemValue(SlopeMonitorItem item, float itemValue, Long sensorId) throws Exception {
        Map<String, Object> map = redisCache.getCacheMap("sensorData_" + sensorId + ""); //读取当前以sensor的id为主键的值

        itemValue = testRandom_generatingFloatBounded_withRange(0, item.getThreshold3());//随机获取生成值 TODO 实际环境运用设备通讯实时值，此处去掉
        String arr;
        if (map.get(item.getItemName()) == null || map.get(item.getItemName()).equals("")) {
            arr = itemValue + "";
        } else {
            arr = map.get(item.getItemName()).toString();//获取以itemName为主键的数组值
            arr += "," + itemValue;
        }
        handleUpdateMoniterItem(item.getItemId(), itemValue);//todo 实际需要考虑是否需要此业务
        handleAlarm(item, itemValue, sensorId);//todo 判断当前设备值 是否含有报警业务，注意当前未写入推送业务
        map.put(item.getItemName(), arr);
        logger.info("sensorData_" + sensorId + ":" + getMapToString(map));
        redisCache.setCacheMap("sensorData_" + sensorId + "", map);//再写入redis 刷新更新值


        //处理写入数据库业务
        String now = DateUtils.dateTimeNow(DateUtils.YYYY_MM_DD_HH_MM_SS);
        //1小时判断一次
        if (now.substring(14, 16).equals("00")) {
            insertHistory(item, sensorId);
        }
    }

    /**
     * 每个小时将redis的传感器检测项数据更新到数据库
     *
     * @param item     检测项
     * @param sensorId 传感器id
     */
    public void insertHistory(SlopeMonitorItem item, Long sensorId) {
        logger.info("写入历史数据:" + DateUtils.dateTimeNow(DateUtils.YYYY_MM_DD_HH_MM_SS));
        Map<String, Object> map = redisCache.getCacheMap("sensorData_" + sensorId + ""); //读
        if (map != null && map.get(item.getItemName()) != null) {
            String arr = map.get(item.getItemName()).toString();
            SlopeItemHistory history = new SlopeItemHistory();
            Map<String, Object> hisMap = new HashMap<>();
            String date = "";
            try {
                hisMap = jdbcTemplate.queryForMap(" select * from slope_item_history where date = (select max(date) from slope_item_history where item_id = '" + item.getItemId() + "') and item_id = '" + item.getItemId() + "' ");
                date = hisMap.get("date").toString();
            } catch (Exception e) {

            } finally {
                if (date.equals(DateUtils.getDate())) {
                    history = slopeItemHistoryService.selectSlopeItemHistoryById(Long.valueOf(hisMap.get("item_history_id").toString()));
                    String lastArr = history.getItemValueArr();
                    if (lastArr.equals("")) {
                        history.setItemValueArr(arr);
                    } else {
                        history.setItemValueArr(history.getItemValueArr() + "," + arr);
                        map.put(item.getItemName(), "");
                    }
                    slopeItemHistoryService.updateSlopeItemHistory(history);
                } else {
                    history.setItemValueArr(arr);
                    history.setItemId(item.getItemId());
                    history.setDate(new Date());
                    slopeItemHistoryService.insertSlopeItemHistory(history);
                }
            }
        }
        redisCache.setCacheMap("sensorData_" + sensorId + "", map);//再写入redis 刷新更新值
    }


    /**
     * 判断实时值是否存在告警情况
     *
     * @param item      检测项
     * @param itemValue 实时监测值
     * @param sensorId  传感器id
     */
    private int handleAlarm(SlopeMonitorItem item, float itemValue, Long sensorId) {
        try {
            String itemId = String.valueOf(item);
            itemValue = Math.abs(itemValue);
            SlopeAlarm alarm = new SlopeAlarm();
            float thread1 = item.getThreshold1();
            float thread2 = item.getThreshold2();
            float thread3 = item.getThreshold3();
            if (itemValue < thread1) { //并未告警
                alarmLevelMap.remove(itemId);
                return 0;
            } else if (itemValue < thread2) {
                alarm.setAlarmLevel(1);
                alarm.setAlarmThreadhold(thread1);
            } else if (itemValue < thread3) {
                alarm.setAlarmLevel(2);
                alarm.setAlarmThreadhold(thread2);
            } else {
                alarm.setAlarmLevel(3);
                alarm.setAlarmThreadhold(thread3);
            }
            alarm.setAlarmTime(new Date());
            alarm.setAlarmItem(item.getItemId());
            alarm.setAlarmValue(itemValue);
            alarm.setAlarmState(0);
            alarm.setCreateTime(new Date());
            alarm.setAlarmSensorId(sensorId);

            int iRet = 0;
            //检测是否已告警
            if (alarmLevelMap.containsKey(itemId)) {
                Integer lastLevel = alarmLevelMap.get(itemId);
                Integer currentLevel = alarm.getAlarmLevel();
                if (currentLevel > lastLevel) {
                    alarmLevelMap.put(itemId, currentLevel);
                    iRet = slopeAlarmService.insertSlopeAlarm(alarm);
                }
            } else {
                alarmLevelMap.put(itemId, alarm.getAlarmLevel());
                iRet = slopeAlarmService.insertSlopeAlarm(alarm);
            }
            return iRet;
        } catch (Exception e) {
            logger.info("设备告警业务异常");
            return 0;
        }
    }

    /**
     * 更新monitor_item的实时值，
     *
     * @param itemId//monitor_item 的列id
     * @param itemValue//检测项的实时值
     */
    private void handleUpdateMoniterItem(Long itemId, float itemValue) {
        SlopeMonitorItem slopeMonitorItem = new SlopeMonitorItem();
        slopeMonitorItem.setItemId(itemId);
        slopeMonitorItem.setItemValue(itemValue);
        slopeMonitorItem.setValueTime(new Date());
        slopeMonitorItemService.updateSlopeMonitorItem(slopeMonitorItem);
    }


    /**
     * 随机生成min~max的随机float数
     *
     * @param min
     * @param max
     * @return
     * @throws Exception
     */
    public float testRandom_generatingFloatBounded_withRange(float min, float max) throws Exception {

        float floatBounded = min + new SecureRandom().nextFloat() * (max - min);
        return floatBounded;
    }

    @RequestMapping("viewRedis")
    public Map<String, Object> viewRedis() {
        Map<String, Object> recMap = new HashMap<>();
        SlopeSensor sensor = new SlopeSensor();
        List<SlopeSensor> list = slopeSensorService.selectSlopeSensorList(sensor);
        for (SlopeSensor slopeSensor : list) {
            if (redisCache.getCacheMap("sensorData_" + slopeSensor.getSensorId() + "").size() != 0) {
                recMap.put(("sensorData_" + slopeSensor.getSensorId() + ""), redisCache.getCacheMap("sensorData_" + slopeSensor.getSensorId() + ""));
                List<SlopeMonitorItem> items = slopeSensor.getMonitorItems();
                Map<String, Object> map = redisCache.getCacheMap("sensorData_" + slopeSensor.getSensorId() + ""); //读取当前以sensor的id为主键的值
                for (SlopeMonitorItem item : items) {
                    recMap.put(("item_" + item.getItemId() + ""), map.get(item.getItemName()).toString().split(",").length);
                }
            }
        }

        return recMap;
    }

    /**
     * Map转String
     *
     * @param map
     * @return
     */
    public static String getMapToString(Map<String, Object> map) {
        Set<String> keySet = map.keySet();
        //将set集合转换为数组
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        //给数组排序(升序)
        Arrays.sort(keyArray);
        //因为String拼接效率会很低的，所以转用StringBuilder
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < keyArray.length; i++) {
            // 参数值为空，则不参与签名 这个方法trim()是去空格
            if ((String.valueOf(map.get(keyArray[i]))).trim().length() > 0) {
                sb.append(keyArray[i]).append(":").append(String.valueOf(map.get(keyArray[i])).trim());
            }
            if (i != keyArray.length - 1) {
                sb.append(",");
            }
        }
        return sb.toString();
    }

    public static String getOutLineMessageInfo(String time, String deviceName, String deviceNum) {
        return time + "，" + deviceName + "设备，编号" + deviceNum + "，已离线。";
    }

}
